local oo = require("SharedLibs.BaseLib");

ISequence = require("SharedLibs.FP.ISequence");
PrintTable = require("SharedLibs.Utils.PrintTable");

--[[
注意: 一个Seq是一个惰性列表(类似Generator).  
	在这里, 通过function实现Generator:
	每次调用function, 返回一个数值. 如果返回nil则列表结束。
				
	所以, 最简单的Seq可以通过一个普通的function构造出来
	
	如果希望function每次返回的数值不同, 可以通过 seq.fromYield() 实现,例如
	
	local mySeq = Seq.fromYield(function()
		local i = 1;
		while true do
			coroutine.yield(i);
			i = i + 1;
		end		
	end)

]]
Sequence = oo.class();

function Sequence:constructor(creator)
	self._creator = creator;
	self.GetIter = self._creator;
end

function Sequence:GetList()
	local fn = self._creator();
	return function() 
		local success, val = fn();
		if success then
			return val;
		end
	end
end

function Sequence:ToArray()
	local arr = {};
	local gFn = self._creator();
	local success, v = gFn();
	while success do
		table.insert(arr, v);
		success, v = gFn();
	end
	return arr;
end

----------------
--- 把一个Seq转换为一个连续发射数据的Signal
--- delay为首次发射延迟，interval为每次发射间隔. 这两个参数必须有一个不为nil(否则生成的Signal立刻就触发完毕了)
--- 返回的第二个参数是用来驱动Signal的coroutine，可以用这个co来停止Signal的发射
---
--- 这个方法要在 SequenceExtension 里实现!!!! 否则CoroLib里无法引用Sequence(循环引用)
---
-- function Sequence:ToSignal(interval, delay)
-- 	interval = interval or 1;
-- 	delay = delay or 1;
-- 	assert(interval or delay);
-- 	local signal = Signal.new();
-- 	local getNext = self._creator();
-- 	local co = co.Run(function() 
-- 		if delay then
-- 			co.Delay(delay);
-- 		end
		
-- 		local success, v = getNext();
-- 		while success do
-- 			signal:Emit(v);			
-- 			if interval then
-- 				co.Delay(interval);
-- 			end			
-- 			success, v = getNext();
-- 		end
-- 	end)
-- 	return signal, co;
-- end

function Sequence:ForEach(fn)
	--- g is GetNext
	local g = self._creator();
	local success, v = g();
	while success do
		fn(v);
		success, v = g();
	end
end

--- pFn: print function
function Sequence:Print(pFn)
	local p = pFn or print;
	local getNext = self._creator();
	local success, v = getNext();
	while success do
		if typeof(v) == "table" then
			p(PrintTable(v));
		else
			p(tostring(v));
		end
		success, v = getNext();
	end
end

function Sequence:Reduce(reducer, initVal)
	local gFn = self._creator();
	local val = initVal;	
	if val == nil then
		local success, v = gFn();
		if success then
			val = v;
		else
			return nil;
		end 
	end;

	local success, v = gFn();
	while success do
		val = reducer(val, v);
		success, v = gFn();
	end
	return val;
end

function Sequence:Filter(filter)
	local creator = function()
		local getNext = self._creator();		
		return function()
			local ret, val = true, nil;
			while ret do
				ret, val = getNext();
				if ret then
					if filter(val) then
						return true, val;
					else
						--- 当前Val被过滤了，就继续检查下一个
						goto continue;
					end
				else
					return false;
				end
				::continue::
			end
		end
	end
	return Sequence.new(creator)
end

function Sequence:Map(mapper)
	local creator = function()
		local getNext = self._creator();
		local m = mapper;
		return function()
			local ret, val = getNext();
			if ret then
				return true, m(val);
			else
				return false;
			end
		end
	end
	return Sequence.new(creator)
end

function Sequence:Take(n)
	local creator = function()
		local getNext = self._creator();
		local i = 0;
		return function()
			if i < n then
				i = i + 1;
				return getNext();
			else
				return false;
			end
		end
	end
	return Sequence.new(creator)
end

function Sequence:Skip(n)
	assert(n > 1);
	local creator = function()
		local getNext = self._creator();
		local i = n;
		return function()
			if i > 0 then
				while i > 0 do
					i = i - 1;
					local ret, val = getNext();
					if not ret then
						return false				
					end
				end
			end
			return getNext();			
		end
	end
	return Sequence.new(creator)
end

function Sequence:Chain(seq)
	local creator = function() 
		--- GetNext1
		local g1 = self:GetIter();
		--- GetNext2
		local g2 = seq:GetIter();
		
		return function() 
			local ret, 	val = g1();
			if ret then
				return true, val;
			else
				return g2();
			end
		end		
	end
	
	return Sequence.new(creator);
end

Sequence.Merge = Sequence.Chain;

function Sequence:Zip(seq)
	local creator = function() 
		local g1 = self:GetIter();
		local g2 = seq:GetIter();

		return function() 
			local ret1, val1 = g1();
			local ret2, val2 = g2();
			if ret1 and ret2 then
				return true, {val1, val2};
			else
				return false;
			end
		end		
	end

	return Sequence.new(creator);
end

function Sequence:Interleave(seq)
	local creator = function() 
		local i1 = self:GetIter();
		local i2 = seq:GetIter();
		local i = i1;
		return function() 
			local ret, val = i();
			if ret then
				if i == i1 then
					i = i2;
				else
					i = i1;
				end
				return true, val;
			else
				return false;
			end
		end		
	end

	return Sequence.new(creator);
end

---------------
Seq = {}

--- count指惰性列表包含几个数组元素
function Seq.fromArray(array, count)
	--- 用来生成Sequence的函数
	local creator = function() 
		count = count or #array;
		local i=1;
		return function() 
			if i <= count then
				local v = array[i];
				i = i + 1;
				return true, v;
			else
				return false;
			end
		end
	end	
		
	return Sequence.new(creator);
end

function Seq.fromYield(func)
	local c = function()
		local co = coroutine.create(func);		
		return function() 
			local success, val = coroutine.resume(co);
			if success and val then
				return true, val;
			else
				return false;
			end		
		end
	end

	return Sequence.new(c)
end

--- asTuple: 每个元素都以二元组数组形式返回（{key, value}）
--- 默认只返回table的value(不返回key)
function Seq.fromTable(tb, asTuple)
	local c = function() 
		local gen, param, key = pairs(tb);
		local val = nil;
		local isEnd = false;
		local asT = asTuple;		
		return function()
			if isEnd then
				return false;
			else
				-- Next iteration
				key, val = gen(tb, key);
				if key == nil then
					isEnd = true;
					return false;
				else
					if asT then
						return true, {key, val};
					else
						return true, val;
					end
				end
			end
		end
	end

	return Sequence.new(c);
end

--- 重复指定参数的序列n次
--- 注意：这是一个无限序列，不要ForEach或Print，会死锁。可以先Take后再遍历
function Seq.duplicate(...)
	local args = {...};
	return Seq.fromYield(function()
		while true do
			for i, v in ipairs(args) do
				coroutine.yield(v);
			end
		end
	end)
end

--- 重复一个Sequence
--- 注意：这是一个无限序列，不要ForEach或Print，会死锁。可以先Take后再遍历
function Seq.cycle(seq)
	return Seq.fromYield(function()		
		while true do
			local getNext = seq:GetIter();
			local success, v = getNext();
			while success do
				coroutine.yield(v);
				success, v = getNext();
			end
		end
	end)
end

function Seq.range(start, finish)
	local c = function() 
		local i = start - 1;
		return function()
			i = i + 1;
			if i > finish then 
				return false;
			else 
				return true, i; 
			end
		end
	end
	return Sequence.new(c);
end

function Seq.random(m, n, seed)	
	local c = function() 
		if seed then
			math.randomseed(seed);
		end
		return function()
			return true, math.random(m, n);
		end
	end
	return Sequence.new(c);
end

return Seq;
